﻿using codeparser.net.v2.Entities;
using codeparser.net.v2.Exceptions;
using codeparser.net.v2.Validators;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace codeparser.net.v2.Tests
{
    /// <summary>
    /// Contains the tests for the analyzer.
    /// </summary>
    [TestClass]
    public class AnalyzerTests
    {
        /// <summary>
        /// Contains the analyzer.
        /// </summary>
        private Analyzer.Analyzer _analyzer;

        /// <summary>
        /// Contains the tag configuration.
        /// </summary>
        private TagConfiguration _tagConfiguration;

        /// <summary>
        /// Initializes a test.
        /// </summary>
        [TestInitialize()]
        public void InitializeTest()
        {
            // Create a tag configuration.
            this._tagConfiguration = new TagConfiguration();
            this._tagConfiguration.Add(new Tag(
                new TagElement("b"), TagClosingStyle.Required));
            this._tagConfiguration.Add(new Tag(
                new TagElement("flagEU"), TagClosingStyle.None));
            this._tagConfiguration.Add(new Tag(
                new TagElement("url"), TagClosingStyle.Required,
                new TagAttribute("href", AttributeStyles.Required | AttributeStyles.Default, AttributeValueStyles.Required),
                new TagAttribute("target", AttributeStyles.Optional, AttributeValueStyles.Required),
                new TagAttribute("enabled", AttributeStyles.Optional, AttributeValueStyles.None)));
            this._tagConfiguration.Add(new Tag(
                new TagElement("url2"), TagClosingStyle.Required,
                new TagAttribute("href2", AttributeStyles.Required | AttributeStyles.Default, AttributeValueStyles.Required),
                new TagAttribute("target2", AttributeStyles.Optional, AttributeValueStyles.Required),
                new TagAttribute("enabled2", AttributeStyles.Optional, AttributeValueStyles.None)));

            // Create an analyzer.
            this._analyzer = new Analyzer.Analyzer(TokenValidatorStrategy.CaseSensitive, this._tagConfiguration);
        }

        /// <summary>
        /// Tests null input.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestNull()
        {
            List<Token> input = null;

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests empty input.
        /// </summary>
        [TestMethod]
        public void TestEmpty()
        {
            List<Token> input = new List<Token>();

            ITreeNode actual = this._analyzer.CreateParseTree(input);

            Assert.AreEqual(typeof(TreeNodeText), actual.GetType());
            Assert.AreEqual("", ((TreeNodeText)actual).Value);
        }

        /// <summary>
        /// Tests a single text token.
        /// </summary>
        [TestMethod]
        public void TestSingleTextToken()
        {
            Token token = new Token(TokenType.Text);
            token.Value = "Hallo Welt!";

            List<Token> input = new List<Token>();
            input.Add(token);

            ITreeNode actual = this._analyzer.CreateParseTree(input);

            Assert.AreEqual(typeof(TreeNodeTag), actual.GetType());
            Assert.AreEqual(1, ((TreeNodeTag)actual).Nodes.Count);
            Assert.AreEqual("Hallo Welt!", ((TreeNodeText)((TreeNodeTag)actual).Nodes[0]).Value);
        }

        /// <summary>
        /// Tests multiple text tokens.
        /// </summary>
        [TestMethod]
        public void TestMultipleTextTokens()
        {
            Token tokenA = new Token(TokenType.Text);
            Token tokenB = new Token(TokenType.Text);
            tokenA.Value = "Hallo ";
            tokenB.Value = "Welt!";

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);

            ITreeNode actual = this._analyzer.CreateParseTree(input);

            Assert.AreEqual(typeof(TreeNodeTag), actual.GetType());
            Assert.AreEqual(2, ((TreeNodeTag)actual).Nodes.Count);
            Assert.AreEqual("Hallo ", ((TreeNodeText)((TreeNodeTag)actual).Nodes[0]).Value);
            Assert.AreEqual("Welt!", ((TreeNodeText)((TreeNodeTag)actual).Nodes[1]).Value);
        }

        /// <summary>
        /// Tests an invalid tag nesting.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidTagNestingException))]
        public void TestInvalidTagNesting()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Element);
            tokenA.Value = "b";
            tokenA.Tag = this._tagConfiguration.Tags["b"];
            tokenB.Value = "/url";
            tokenB.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a valid tag nesting.
        /// </summary>
        [TestMethod]
        public void TestValidTagNesting()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Element);
            tokenA.Value = "b";
            tokenA.Tag = this._tagConfiguration.Tags["b"];
            tokenB.Value = "/b";
            tokenB.Tag = this._tagConfiguration.Tags["b"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests an invalid tag nesting.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidTagNestingException))]
        public void TestNestedInvalidTagNesting()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Element);
            Token tokenC = new Token(TokenType.Element);
            Token tokenD = new Token(TokenType.Element);
            tokenA.Value = "b";
            tokenA.Tag = this._tagConfiguration.Tags["b"];
            tokenB.Value = "url";
            tokenB.Tag = this._tagConfiguration.Tags["url"];
            tokenC.Value = "/b";
            tokenC.Tag = this._tagConfiguration.Tags["b"];
            tokenD.Value = "/url";
            tokenD.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);
            input.Add(tokenC);
            input.Add(tokenD);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a valid tag nesting.
        /// </summary>
        [TestMethod]
        public void TestNestedValidTagNesting()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Attribute);
            Token tokenC = new Token(TokenType.AttributeValue);
            Token tokenD = new Token(TokenType.Element);
            Token tokenE = new Token(TokenType.Element);
            Token tokenF = new Token(TokenType.Element);
            tokenA.Value = "url";
            tokenA.Tag = this._tagConfiguration.Tags["url"];
            tokenB.Value = "href";
            tokenC.Value = "http://www.goloroden.de";
            tokenD.Value = "b";
            tokenD.Tag = this._tagConfiguration.Tags["b"];
            tokenE.Value = "/b";
            tokenE.Tag = this._tagConfiguration.Tags["b"];
            tokenF.Value = "/url";
            tokenF.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);
            input.Add(tokenC);
            input.Add(tokenD);
            input.Add(tokenE);
            input.Add(tokenF);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a missing closing tag.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(MissingClosingTagException))]
        public void TestMissingClosingTag()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Element);
            Token tokenC = new Token(TokenType.Element);
            tokenA.Value = "url";
            tokenA.Tag = this._tagConfiguration.Tags["url"];
            tokenB.Value = "b";
            tokenB.Tag = this._tagConfiguration.Tags["b"];
            tokenC.Value = "/b";
            tokenC.Tag = this._tagConfiguration.Tags["b"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);
            input.Add(tokenC);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a missing opening tag.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(MissingOpeningTagException))]
        public void TestMissingOpeningTag()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Element);
            Token tokenC = new Token(TokenType.Element);
            tokenA.Value = "b";
            tokenA.Tag = this._tagConfiguration.Tags["b"];
            tokenB.Value = "/b";
            tokenB.Tag = this._tagConfiguration.Tags["b"];
            tokenC.Value = "/url";
            tokenC.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);
            input.Add(tokenC);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a non matching element and attribute.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidAttributeException))]
        public void TestNonMatchingElementAndAttribute()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Attribute);
            Token tokenC = new Token(TokenType.Element);
            tokenA.Value = "url";
            tokenA.Tag = this._tagConfiguration.Tags["url"];
            tokenB.Value = "href2";
            tokenC.Value = "/url";
            tokenC.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);
            input.Add(tokenC);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a missing required attribute.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(RequiredAttributeMissingException))]
        public void TestMissingRequiredAttribute()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Element);
            tokenA.Value = "url";
            tokenA.Tag = this._tagConfiguration.Tags["url"];
            tokenB.Value = "/url";
            tokenB.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a missing required attribute value.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(RequiredAttributeValueMissingException))]
        public void TestMissingRequiredAttributeValue()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Attribute);
            Token tokenC = new Token(TokenType.Element);
            tokenA.Value = "url";
            tokenA.Tag = this._tagConfiguration.Tags["url"];
            tokenB.Value = "href";
            tokenC.Value = "/url";
            tokenC.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);
            input.Add(tokenC);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }

        /// <summary>
        /// Tests a specified, but needless attribute value.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(NeedlessAttributeValueSpecifiedException))]
        public void TestSpecifiedNeedlessAttributeValue()
        {
            Token tokenA = new Token(TokenType.Element);
            Token tokenB = new Token(TokenType.Attribute);
            Token tokenC = new Token(TokenType.AttributeValue);
            Token tokenD = new Token(TokenType.Attribute);
            Token tokenE = new Token(TokenType.AttributeValue);
            Token tokenF = new Token(TokenType.Element);
            tokenA.Value = "url";
            tokenA.Tag = this._tagConfiguration.Tags["url"];
            tokenB.Value = "href";
            tokenC.Value = "http://www.goloroden.de";
            tokenD.Value = "enabled";
            tokenE.Value = "true";
            tokenF.Value = "/url";
            tokenF.Tag = this._tagConfiguration.Tags["url"];

            List<Token> input = new List<Token>();
            input.Add(tokenA);
            input.Add(tokenB);
            input.Add(tokenC);
            input.Add(tokenD);
            input.Add(tokenE);
            input.Add(tokenF);

            ITreeNode actual = this._analyzer.CreateParseTree(input);
        }
    }
}